/*
 * flmparse.c
 *
 *  Created on: 2021年4月10日
 *      Author: hello
 */

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include "elf.h"

#define FILENAME "STM32F4xx_2048.FLM"

static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size);
static int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage);

int main(int argc, char const *argv[])
{
	int i = 0;
	uint32_t RAM[256] = {0};
	uint32_t Addr[5] = {0};
	uint32_t Size = 0;

	if(argc != 2)
	{
		printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
		printf("Usage:\n");
		printf("        flmparse.exe [filename]\n");
		printf("\n\n");
		goto __exit;
	}

	/* 这8个数是中断halt程序，让函数执行完后返回到这里来执行从而让CPU自动halt住 */
	RAM[0] = 0xE00ABE00;
	RAM[1] = 0x062D780D;
	RAM[2] = 0x24084068;
	RAM[3] = 0xD3000040;
	RAM[4] = 0x1E644058;
	RAM[5] = 0x1C49D1FA;
	RAM[6] = 0x2A001E52;
	RAM[7] = 0x4770D1F2;

	if(FLM_Prase(argv[1], &RAM[8], &Size, &Addr[0],&Addr[1],&Addr[2],&Addr[3],&Addr[4]) < 0)
	{
		printf("错误：解析FLM格式文件失败，请检查FLM文件是否存在或格式正确性！\r\n");
		goto __exit;
	}

	fprintf(stdout, "\r\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");

	Size += 32;

	fprintf(stdout, "\r\nstatic const uint32_t flash_code[] = \n{");
	for(i = 0; i < (Size >> 2); i++)
	{
		if(i % 8 == 0)
		{
			fprintf(stdout, "\n    ");
		}
		fprintf(stdout, "0X%08X,", RAM[i]);
	}
	fprintf(stdout, "\n};\n");

	fprintf(stdout, "\r\nconst program_target_t flash_algo =\n{\n");
	fprintf(stdout, "    0X%08X,  // Init\n",        Addr[0] + 0X20000020);
	fprintf(stdout, "    0X%08X,  // UnInit\n",      Addr[1] + 0X20000020);
	fprintf(stdout, "    0X%08X,  // EraseChip\n",   Addr[2] + 0X20000020);
	fprintf(stdout, "    0X%08X,  // EraseSector\n", Addr[3] + 0X20000020);
	fprintf(stdout, "    0X%08X,  // ProgramPage\n", Addr[4] + 0X20000020);
	fprintf(stdout, "\n");
	fprintf(stdout, "    // BKPT : start of blob + 1\n");
	fprintf(stdout, "    // RSB  : address to access global/static data\n");
	fprintf(stdout, "    // RSP  : stack pointer\n");
	fprintf(stdout, "    {\n");
	fprintf(stdout, "        0X20000001,\n");
	fprintf(stdout, "        0X20000C00,\n");
	fprintf(stdout, "        0X20001000,\n");
	fprintf(stdout, "    },\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "    0x20000400,                      // mem buffer location\n");
	fprintf(stdout, "    0x20000000,                      // location to write prog_blob in target RAM\n");
	fprintf(stdout, "    sizeof(flash_code),              // prog_blob size\n");
	fprintf(stdout, "    flash_code,                      // address of prog_blob\n");
	fprintf(stdout, "    0x00000400,                      // ram_to_flash_bytes_to_be_written\n");
	fprintf(stdout, "};\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");

__exit:
	return 0;
}


static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size)
{
	int ret = 0;
	int fd = 0;

	if ((fd = open(FName, O_RDONLY | O_BINARY)) < 0)
	{
		ret = -1;
		goto __exit;
	}

	if (lseek(fd, offset, SEEK_SET) < 0)
	{
		ret = -2;
		goto __exit;
	}

	if (read(fd, buf, size) != size)
	{
		ret = -3;
		goto __exit;
	}

__exit: 
	close(fd);
	return ret;
}

int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage)
{
#define LOAD_FUN_NUM 5

	uint8_t buffer[1024] = {0};
	int i = 0, k = 0;
	int found = 0;
	const Elf32_Phdr* pPhdr = (const Elf32_Phdr *) buffer;
	const Elf32_Shdr* pShdr = (const Elf32_Shdr *) buffer;
	const Elf32_Sym* pSymbol = (const Elf32_Sym *) buffer;
	Elf32_Ehdr ehdr = {0};      // ELF文件信息头
	Elf32_Shdr ShdrSym = {0};   // 符号表头
	Elf32_Shdr ShdrStr = {0};   // 字符串表头
	const char* StrFunNameTable[LOAD_FUN_NUM] = { "Init", "UnInit", "EraseChip", "EraseSector", "ProgramPage"};
	int StrFunIndexTable[LOAD_FUN_NUM] = {-1, -1, -1, -1, -1};

	//
	// 读取ELF文件头信息（ELF Header）
	//
	ReadDataFromFile(FName, 0, &ehdr, sizeof(Elf32_Ehdr));

	// 不是ELF格式文件
	if (strstr((const char *)ehdr.e_ident, "ELF") == NULL)
	{
		return -1;
	}

	//
	// 读取程序头信息（Program Header）
	//
	ReadDataFromFile(FName, ehdr.e_phoff, buffer, sizeof(Elf32_Phdr) * ehdr.e_phnum);
	for (i = 0; i < ehdr.e_phnum; i++)
	{
		if (pPhdr[i].p_type == PT_LOAD && (pPhdr[i].p_flags & (PF_X | PF_W | PF_R)) == (PF_X | PF_W | PF_R))
		{
			if (pPhdr[i].p_filesz > sizeof(buffer))  // RAM代码过大
			{
				return -2;
			}
			if(ReadDataFromFile(FName, pPhdr[i].p_offset, pBuffer, pPhdr[i].p_filesz) < 0)  // 提取需要下载到RAM的程序代码
			{
				return -3;
			}
		printf("====:%d\r\n", pPhdr[i].p_filesz);
			*Size = pPhdr[i].p_filesz;
		}
	}

	//
	// 读取节区头部（Sections Header）
	//
	ReadDataFromFile(FName, ehdr.e_shoff, buffer, sizeof(Elf32_Shdr) * ehdr.e_shnum);

	// 查找符号表头并拷贝出来备用
	for (i = 0; i < ehdr.e_shnum; i++)
	{
		if (pShdr[i].sh_type == SHT_SYMTAB)
		{
			memcpy(&ShdrSym, &pShdr[i], sizeof(Elf32_Shdr));

			// 查找字符串表头并拷贝出来备用
			if (pShdr[ShdrSym.sh_link].sh_type == SHT_STRTAB)
			{
				memcpy(&ShdrStr, &pShdr[ShdrSym.sh_link], sizeof(Elf32_Shdr));
				found = 1;
				break;
			}
		}
	}

	if(!found)
	{
		return -4;
	}

	//
	// 根据字符串表头读取所有字符串表
	//
	ReadDataFromFile(FName, ShdrStr.sh_offset, buffer, ShdrStr.sh_size);
	for (i = 0; i < ShdrStr.sh_size; i++)    if (buffer[i] == '\0')    buffer[i] = '\n';
	buffer[ShdrStr.sh_size] = 0;
	for (i = 0; i < LOAD_FUN_NUM; i++)
	{
		char* p = NULL;
		if (StrFunNameTable[i] != NULL && (p = strstr((const char *) buffer, StrFunNameTable[i])) != NULL)
		{
			StrFunIndexTable[i] = (uint32_t) p - (uint32_t) buffer;
		}
	}

	//
	// 读取符号表
	//
	ReadDataFromFile(FName, ShdrSym.sh_offset, buffer, ShdrSym.sh_size);

	// 遍历查询我们用到的函数符号
	for (i = 0; i < ShdrSym.sh_size / sizeof(Elf32_Sym); i++, pSymbol++)
	{
		for (k = 0; k < LOAD_FUN_NUM; k++)
		{
			if (StrFunIndexTable[k] >= 0 && StrFunIndexTable[k] == pSymbol->st_name)  // symbol.st_name的值就是偏移地址
			{
				switch (k)
				{
				case 0:
					*Init = pSymbol->st_value;
					break;
				case 1:
					*UnInit = pSymbol->st_value;
					break;
				case 2:
					*EraseChip = pSymbol->st_value;
					break;
				case 3:
					*EraseSector = pSymbol->st_value;
					break;
				case 4:
					*ProgramPage = pSymbol->st_value;
					break;
				default:
					break;
				}
			}
		}
	}

	return 0;
}

